#pragma once

#include <iostream>
#include <sstream>

#define SUPPORT_DEBUG true

typedef enum {
  LOG_LEVEL_DEBUG = 0,
  LOG_LEVEL_INFO,
  LOG_LEVEL_ERROR
} LOG_LEVEL;

class LogStream {
public:
  LogStream() = default;
  ~LogStream() = default;

  template <typename T>
  LogStream &operator<<(const T &val) noexcept
  {
      sstream_ << val;
      return *this;
  }

  LogStream &operator<<(std::ostream& (*func)(std::ostream &os)) noexcept
  {
      sstream_ << func;
      return *this;
  }

  friend class LogWriter;
private:
  std::stringstream sstream_;
};

class LogWriter {
public:
  LogWriter(std::string func, uint32_t line, uint32_t logLv) : func_(func), line_(line), logLv_(logLv) {}
  ~LogWriter() = default;

  void operator<(const LogStream &stream) const noexcept
  {
#ifdef SUPPORT_DEBUG
      std::ostringstream msg;
      msg << "[" << func_ << " " << line_ << "]";
      switch (logLv_) {
        case LOG_LEVEL_INFO: msg << "[INFO]"; break;
        case LOG_LEVEL_ERROR: msg << "[ERROR]"; break;
      }
      msg << stream.sstream_.rdbuf();
      std::cout<< msg.str() << std::endl;
#endif
  }

private:
  std::string func_;
  uint32_t line_;
  uint32_t logLv_;
};

#define LOG_ERR LogWriter(std::string(__FUNCTION__), __LINE__, LOG_LEVEL_ERROR) < LogStream()
#define LOG_INFO  LogWriter(std::string(__FUNCTION__), __LINE__, LOG_LEVEL_INFO) < LogStream()
#define LOG_DEBUG LogWriter(std::string(__FUNCTION__), __LINE__, LOG_LEVEL_DEBUG) < LogStream()